--[[
	Lua 阿拉伯数字转中文实现 https://blog.csdn.net/lp12345678910/article/details/121396243
	农历功能复制自 https://github.com/boomker/rime-fast-xhup
--]]

-- 数字转中文：

local numerical_units = {
	"",
	"十",
	"百",
	"千",
	"万",
	"十",
	"百",
	"千",
	"亿",
	"十",
	"百",
	"千",
	"兆",
	"十",
	"百",
	"千",
}

local numerical_names = {
	"零",
	"一",
	"二",
	"三",
	"四",
	"五",
	"六",
	"七",
	"八",
	"九",
}

local function convert_arab_to_chinese(number)
	local n_number = tonumber(number)
	assert(n_number, "传入参数非正确number类型!")

	-- 0 ~ 9
	if n_number < 10 then
		return numerical_names[n_number + 1]
	end
	-- 一十九 => 十九
	if n_number < 20 then
		local digit = string.sub(n_number, 2, 2)
		if digit == "0" then
			return "十"
		else
			return "十" .. numerical_names[digit + 1]
		end
	end

	--[[
        1. 最大输入9位
            超过9位，string的len加2位（因为有.0的两位）
            零 ~ 九亿九千九百九十九万九千九百九十九
            0 ~ 999999999
        2. 最大输入14位（超过14位会四舍五入）
            零 ~ 九十九兆九千九百九十九亿九千九百九十九万九千九百九十九万
            0 ~ 99999999999999
    --]]
	local len_max = 9
	local len_number = string.len(number)
	assert(
		len_number > 0 and len_number <= len_max,
		"传入参数位数" .. len_number .. "必须在(0, " .. len_max .. "]之间！"
	)

	-- 01，数字转成表结构存储
	local numerical_tbl = {}
	for i = 1, len_number do
		numerical_tbl[i] = tonumber(string.sub(n_number, i, i))
	end

	local pre_zero = false
	local result = ""
	for index, digit in ipairs(numerical_tbl) do
		local curr_unit = numerical_units[len_number - index + 1]
		local curr_name = numerical_names[digit + 1]
		if digit == 0 then
			if not pre_zero then
				result = result .. curr_name
			end
			pre_zero = true
		else
			result = result .. curr_name .. curr_unit
			pre_zero = false
		end
	end
	result = string.gsub(result, "零+$", "")
	return result
end

-- 农历：

-- 天干名称
local cTianGan = {
	"甲",
	"乙",
	"丙",
	"丁",
	"戊",
	"己",
	"庚",
	"辛",
	"壬",
	"癸",
}

-- 地支名称
local cDiZhi = {
	"子",
	"丑",
	"寅",
	"卯",
	"辰",
	"巳",
	"午",
	"未",
	"申",
	"酉",
	"戌",
	"亥",
}

-- 属相名称
local cShuXiang = {
	"鼠",
	"牛",
	"虎",
	"兔",
	"龙",
	"蛇",
	"马",
	"羊",
	"猴",
	"鸡",
	"狗",
	"猪",
}

-- 农历日期名
local cDayName = {
	"初一",
	"初二",
	"初三",
	"初四",
	"初五",
	"初六",
	"初七",
	"初八",
	"初九",
	"初十",
	"十一",
	"十二",
	"十三",
	"十四",
	"十五",
	"十六",
	"十七",
	"十八",
	"十九",
	"二十",
	"廿一",
	"廿二",
	"廿三",
	"廿四",
	"廿五",
	"廿六",
	"廿七",
	"廿八",
	"廿九",
	"三十",
}

-- 农历月份名
local cMonName = {
	"正月",
	"二月",
	"三月",
	"四月",
	"五月",
	"六月",
	"七月",
	"八月",
	"九月",
	"十月",
	"冬月",
	"腊月",
}

-- 农历数据
local wNongliData = {
	"AB500D2",
	"4BD0883",
	"4AE00DB",
	"A5700D0",
	"54D0581",
	"D2600D8",
	"D9500CC",
	"655147D",
	"56A00D5",
	"9AD00CA",
	"55D027A",
	"4AE00D2",
	"A5B0682",
	"A4D00DA",
	"D2500CE",
	"D25157E",
	"B5500D6",
	"56A00CC",
	"ADA027B",
	"95B00D3",
	"49717C9",
	"49B00DC",
	"A4B00D0",
	"B4B0580",
	"6A500D8",
	"6D400CD",
	"AB5147C",
	"2B600D5",
	"95700CA",
	"52F027B",
	"49700D2",
	"6560682",
	"D4A00D9",
	"EA500CE",
	"6A9157E",
	"5AD00D6",
	"2B600CC",
	"86E137C",
	"92E00D3",
	"C8D1783",
	"C9500DB",
	"D4A00D0",
	"D8A167F",
	"B5500D7",
	"56A00CD",
	"A5B147D",
	"25D00D5",
	"92D00CA",
	"D2B027A",
	"A9500D2",
	"B550781",
	"6CA00D9",
	"B5500CE",
	"535157F",
	"4DA00D6",
	"A5B00CB",
	"457037C",
	"52B00D4",
	"A9A0883",
	"E9500DA",
	"6AA00D0",
	"AEA0680",
	"AB500D7",
	"4B600CD",
	"AAE047D",
	"A5700D5",
	"52600CA",
	"F260379",
	"D9500D1",
	"5B50782",
	"56A00D9",
	"96D00CE",
	"4DD057F",
	"4AD00D7",
	"A4D00CB",
	"D4D047B",
	"D2500D3",
	"D550883",
	"B5400DA",
	"B6A00CF",
	"95A1680",
	"95B00D8",
	"49B00CD",
	"A97047D",
	"A4B00D5",
	"B270ACA",
	"6A500DC",
	"6D400D1",
	"AF40681",
	"AB600D9",
	"93700CE",
	"4AF057F",
	"49700D7",
	"64B00CC",
	"74A037B",
	"EA500D2",
	"6B50883",
	"5AC00DB",
	"AB600CF",
	"96D0580",
	"92E00D8",
	"C9600CD",
	"D95047C",
	"D4A00D4",
	"DA500C9",
	"755027A",
	"56A00D1",
	"ABB0781",
	"25D00DA",
	"92D00CF",
	"CAB057E",
	"A9500D6",
	"B4A00CB",
	"BAA047B",
	"AD500D2",
	"55D0983",
	"4BA00DB",
	"A5B00D0",
	"5171680",
	"52B00D8",
	"A9300CD",
	"795047D",
	"6AA00D4",
	"AD500C9",
	"5B5027A",
	"4B600D2",
	"96E0681",
	"A4E00D9",
	"D2600CE",
	"EA6057E",
	"D5300D5",
	"5AA00CB",
	"76A037B",
	"96D00D3",
	"4AB0B83",
	"4AD00DB",
	"A4D00D0",
	"D0B1680",
	"D2500D7",
	"D5200CC",
	"DD4057C",
	"B5A00D4",
	"56D00C9",
	"55B027A",
	"49B00D2",
	"A570782",
	"A4B00D9",
	"AA500CE",
	"B25157E",
	"6D200D6",
	"ADA00CA",
	"4B6137B",
	"93700D3",
	"49F08C9",
	"49700DB",
	"64B00D0",
	"68A1680",
	"EA500D7",
	"6AA00CC",
	"A6C147C",
	"AAE00D4",
	"92E00CA",
	"D2E0379",
	"C9600D1",
	"D550781",
	"D4A00D9",
	"DA400CD",
	"5D5057E",
	"56A00D6",
	"A6C00CB",
	"55D047B",
	"52D00D3",
	"A9B0883",
	"A9500DB",
	"B4A00CF",
	"B6A067F",
	"AD500D7",
	"55A00CD",
	"ABA047C",
	"A5A00D4",
	"52B00CA",
	"B27037A",
	"69300D1",
	"7330781",
	"6AA00D9",
	"AD500CE",
	"4B5157E",
	"4B600D6",
	"A5700CB",
	"54E047C",
	"D1600D2",
	"E960882",
	"D5200DA",
	"DAA00CF",
	"6AA167F",
	"56D00D7",
	"4AE00CD",
	"A9D047D",
	"A2D00D4",
	"D1500C9",
	"F250279",
	"D5200D1",
}

-- 十进制转二进制
local function Dec2bin(n)
	local t, t1
	local tables = {}
	t = tonumber(n)
	while math.floor(t / 2) >= 1 do
		t1 = t and math.fmod(t, 2)
		if t1 > 0 then
			if #tables > 0 then
				table.insert(tables, 1, 1)
			else
				tables[1] = 1
			end
		else
			if #tables > 0 then
				table.insert(tables, 1, 0)
			else
				tables[1] = 0
			end
		end
		t = math.floor(t / 2)
		if t == 1 then
			if #tables > 0 then
				table.insert(tables, 1, 1)
			else
				tables[1] = 1
			end
		end
	end
	return string.gsub(table.concat(tables), "^[0]+", "")
end

-- 2/10/16进制互转
local function Atoi(x, inPuttype, outputtype)
	local r
	if tonumber(inPuttype) == 2 then
		if tonumber(outputtype) == 10 then -- 2进制-->10进制
			r = tonumber(tostring(x), 2)
			-- elseif tonumber(outputtype) == 16 then -- 2进制-->16进制
			-- 	r = bin2hex(tostring(x))
		end
	elseif tonumber(inPuttype) == 10 then
		if tonumber(outputtype) == 2 then -- 10进制-->2进制
			r = Dec2bin(tonumber(x))
		elseif tonumber(outputtype) == 16 then -- 10进制-->16进制
			r = string.format("%x", x)
		end
	elseif tonumber(inPuttype) == 16 then
		if tonumber(outputtype) == 2 then -- 16进制-->2进制
			r = Dec2bin(tonumber(tostring(x), 16))
		elseif tonumber(outputtype) == 10 then -- 16进制-->10进制
			r = tonumber(tostring(x), 16)
		end
	end
	return r
end

-- 农历16进制数据分解
local function Analyze(Data)
	local rtn1, rtn2, rtn3, rtn4
	rtn1 = Atoi(string.sub(Data, 1, 3), 16, 2)
	if string.len(rtn1) < 12 then
		rtn1 = "0" .. rtn1
	end
	rtn2 = string.sub(Data, 4, 4)
	rtn3 = Atoi(string.sub(Data, 5, 5), 16, 10)
	rtn4 = Atoi(string.sub(Data, -2, -1), 16, 10)
	if string.len(rtn4) == 3 then
		rtn4 = "0" .. Atoi(string.sub(Data, -2, -1), 16, 10)
	end
	-- string.gsub(rtn1, "^[0]*", "")
	return { rtn1, rtn2, rtn3, rtn4 }
end

-- 年天数判断
local function IsLeap(y)
	local year = tonumber(y)
	if not year then
		return nil
	end
	if math.fmod(year, 400) ~= 0 and math.fmod(year, 4) == 0 or math.fmod(year, 400) == 0 then
		return 366
	else
		return 365
	end
end

-- 返回当年过了多少天
local function leaveDate(y)
	local day, total
	total = 0
	if IsLeap(tonumber(string.sub(y, 1, 4))) > 365 then
		day = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
	else
		day = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
	end
	if tonumber(string.sub(y, 5, 6)) > 1 then
		for i = 1, tonumber(string.sub(y, 5, 6)) - 1 do
			total = total + day[i]
		end
		total = total + tonumber(string.sub(y, 7, 8))
	else
		return tonumber(string.sub(y, 7, 8))
	end
	return tonumber(total)
end

-- 计算日期差，两个8位数日期之间相隔的天数，date2>date1
local function diffDate(date1, date2)
	local n, total
	total = 0
	date1 = tostring(date1)
	date2 = tostring(date2)
	if tonumber(date2) > tonumber(date1) then
		n = tonumber(string.sub(date2, 1, 4)) - tonumber(string.sub(date1, 1, 4))
		if n > 1 then
			for i = 1, n - 1 do
				total = total + IsLeap(tonumber(string.sub(date1, 1, 4)) + i)
			end
			total = total
				+ leaveDate(tonumber(string.sub(date2, 1, 8)))
				+ IsLeap(tonumber(string.sub(date1, 1, 4)))
				- leaveDate(tonumber(string.sub(date1, 1, 8)))
		elseif n == 1 then
			total = IsLeap(tonumber(string.sub(date1, 1, 4)))
				- leaveDate(tonumber(string.sub(date1, 1, 8)))
				+ leaveDate(tonumber(string.sub(date2, 1, 8)))
		else
			total = leaveDate(tonumber(string.sub(date2, 1, 8))) - leaveDate(tonumber(string.sub(date1, 1, 8)))
			-- print(date1 .. "-" .. date2)
		end
	elseif tonumber(date2) == tonumber(date1) then
		return 0
	else
		return -1
	end
	return total
end

-- 公历转农历，支持转化范围公元1900-2100年
-- 公历日期 Gregorian:格式 YYYYMMDD
-- <返回值>农历日期 中文 天干地支属相
local function Date2LunarDate(Gregorian)
	Gregorian = tostring(Gregorian)
	local Year, Month, Day, Pos, Data0, Data1, MonthInfo, LeapInfo, Leap, Newyear, LYear, thisMonthInfo
	Year = tonumber(Gregorian.sub(Gregorian, 1, 4))
	Month = tonumber(Gregorian.sub(Gregorian, 5, 6))
	Day = tonumber(Gregorian.sub(Gregorian, 7, 8))
	if Year > 2100 or Year < 1899 or Month > 12 or Month < 1 or Day < 1 or Day > 31 or string.len(Gregorian) < 8 then
		return "无效日期", "无效日期"
	end

	-- 获取两百年内的农历数据
	Pos = Year - 1900 + 2
	Data0 = wNongliData[Pos - 1]
	Data1 = wNongliData[Pos]
	-- 判断农历年份
	local tb1 = Analyze(Data1)
	MonthInfo = tb1[1]
	LeapInfo = tb1[2]
	Leap = tb1[3]
	Newyear = tb1[4]
	local Date1 = Year .. Newyear
	local Date2 = Gregorian
	local Date3 = diffDate(Date1, Date2) -- 和当年农历新年相差的天数
	if Date3 < 0 then
		-- print(Data0 .. "-2")
		tb1 = Analyze(Data0)
		Year = Year - 1
		MonthInfo = tb1[1]
		LeapInfo = tb1[2]
		Leap = tb1[3]
		Newyear = tb1[4]
		Date1 = Year .. Newyear
		Date2 = Gregorian
		Date3 = diffDate(Date1, Date2)
		-- print(Date2 .. "--" .. Date1 .. "--" .. Date3)
	end

	Date3 = Date3 + 1
	LYear = Year  -- 农历年份，就是上面计算后的值
	if Leap > 0 then -- 有闰月
		thisMonthInfo = string.sub(MonthInfo, 1, tonumber(Leap)) .. LeapInfo .. string.sub(MonthInfo, Leap + 1)
	else
		thisMonthInfo = MonthInfo
	end

	local thisMonth, thisDays, LMonth, LDay, Isleap, LunarDate, LunarDate2, LunarYear, LunarMonth
	for i = 1, 13 do
		thisMonth = string.sub(thisMonthInfo, i, i)
		thisDays = 29 + thisMonth
		if Date3 > thisDays then
			Date3 = Date3 - thisDays
		else
			if Leap > 0 then
				if Leap >= i then
					LMonth = i
					Isleap = 0
				else
					LMonth = i - 1
					if i - Leap == 1 then
						Isleap = 1
					else
						Isleap = 0
					end
				end
			else
				LMonth = i
				Isleap = 0
			end
			LDay = math.floor(Date3)
			break
		end
	end

	if Isleap > 0 then
		LunarMonth = "闰" .. cMonName[LMonth]
	else
		LunarMonth = cMonName[LMonth]
	end

	local _nis = tostring(LYear)
	local _LunarYears = ""
	for i = 1, _nis:len() do
		local _ni_digit = tonumber(_nis:sub(i, i))
		_LunarYears = _LunarYears .. convert_arab_to_chinese(_ni_digit)
	end

	LunarYear = string.gsub(_LunarYears, "零", "〇")
	LunarDate = cTianGan[math.fmod(LYear - 4, 10) + 1]
		.. cDiZhi[math.fmod(LYear - 4, 12) + 1]
		.. "年（"
		.. cShuXiang[math.fmod(LYear - 4, 12) + 1]
		.. "）"
		.. LunarMonth
		.. cDayName[LDay]

	LunarDate2 = LunarYear .. "年" .. LunarMonth .. cDayName[LDay]
	return LunarDate, LunarDate2
end

-- 农历
-- 从 lunar: nl 获取农历触发关键字（双拼默认为 lunar）
-- 从 recognizer/patterns/gregorian_to_lunar 获取第 2 个字符作为公历转农历的触发前缀，默认为 N
local function translator(input, seg, env)
	env.lunar_key_word = env.lunar_key_word or
		(env.engine.schema.config:get_string(env.name_space:gsub('^*', '')) or 'nl')
	env.gregorian_to_lunar = env.gregorian_to_lunar or
		(env.engine.schema.config:get_string('recognizer/patterns/gregorian_to_lunar'):sub(2, 2) or 'N')
	if input == env.lunar_key_word then
		local date1, date2 = Date2LunarDate(os.date("%Y%m%d"))
		local lunar_ymd = (Candidate("", seg.start, seg._end, date2, ""))
		lunar_ymd.quality = 999
		yield(lunar_ymd)
		local lunar_date = Candidate("", seg.start, seg._end, date1, "")
		lunar_date.quality = 999
		yield(lunar_date)
	elseif env.gregorian_to_lunar ~= '' and input:sub(1, 1) == env.gregorian_to_lunar then
		local date1, date2 = Date2LunarDate(input:sub(2))
		local lunar_ymd = (Candidate("", seg.start, seg._end, date2, ""))
		lunar_ymd.quality = 999
		yield(lunar_ymd)
		local lunar_date = Candidate("", seg.start, seg._end, date1, "")
		lunar_date.quality = 999
		yield(lunar_date)
	end
end

return translator